Tips and Tricks for Macsbug

All of us who program on the Macintosh have lots of little tricks that we
use to get our job done. Most of these are passed around via e-mail and the
Usenet News, if they are passed on at all. In an effort to collect all these
in one place for the benefit of all, I've created this page. Please submit
your tips to crawford@scruznet.com and I will add them to this page - with
proper attribution of course.

Contents

Where to get Macsbug
Are there books that teach how to use Macsbug?
Debugging Software on the Macintosh
How to get Macsbug help for free
Breaking While a Particular Application is Executing
Logging Program Execution
Logging Data in Real Time
Using Cursors to Trace Program Execution
Using Touch and Go Breakpoints with Two Monitors
Twiddling Pixels in the Menu Bar
Forcing Testers to Use Macsbug During Beta Testing
The Developer University Debugging Class
Links to Other Macsbug Pages

Where to get Macsbug

     Macsbug is available via anonymous FTP from Apple Computer from here or
     here. It is included with the two books below, but you will want to get
     the latest version, especially if you are using a PowerPC.

Are there books that teach how to use Macsbug?

     Yes, among them are:

        o Othmer and Strauss, Debugging Mac Software with Macsbug,
          Addison-Wesley 1991, ISBN 0201570491. Macsbug included on disk.
        o Apple Computer Inc., Macsbug Reference and Debugging Guide version
          6.2, Addison-Wesley 1991, ISBN 0201567687. Macsbug 6.2 included on
          disk.

     While you don't need to know how to write assembly code to use Macsbug
     effectively, you will need to know how to read and understand it. Thus
     you will also need to get assembly code reference manuals, such as:

        o Kacmarcik, Optimizing PowerPC Code, Addison-Wesley 1995, ISBN
          0201408392.
        o Motorola, M68000UM/AD MC68000 8/16/32-Bit MPU User Manual,
          Motorola Literature Distribution, 1991.

     All of these books may be ordered from the Computer Literacy Bookstore.

How to get Macsbug help for free

     Contributed by Bill Coderre, bc@wetware.com

       1. Install Macsbug and programmer's key
       2. Reboot the machine. Don't start any apps yet.
       3. Hit CMD-POWER
       4. type "log Macsbug Help <RETURN>"
       5. type "help<RETURN>"
       6. Push space until done.
       7. type "log<RETURN>"
       8. type "g<RETURN>"
       9. Find the file called Macsbug Help on your desktop. Open it with a
          text editor. Read it.

Breaking while a particular application is executing

     Most applications call WaitNextEvent. While an application is
     executing, a Pascal string with the name of the application is placed
     at location 0x910. Thus the first four characters of the name itself
     begin at location 0x911. Suppose you want to break while SimpleText is
     active. Enter Macsbug and give the command:

     atb WaitNextEvent 911^ = 'Simp'

     then continue. You will shortly drop into Macsbug at SimpleText's
     WaitNextEvent call. This is particularly useful when debugging faceless
     background applications. If the application does not call
     WaitNextEvent, try GetNextEvent instead.

Logging Program Execution

     Contributed by Darren Giles, Terran Interactive, mars@netcom.com

     Always on the lookout for useful debugging tools & tips, I'd love to
     share ideas with others on the topic. I'll start out by offering a
     snippet I've found very useful -- hopefully others will do the same.

     One thing that's really bugged me about MacsBug on PPC is that the
     stack crawl has become a much less useful tool. The snippet below gives
     you the ability to keep track of a list of the last significant points
     your program has visited. It's a list, not a stack, so you can also see
     patterns of execution.

     Hardly rocket science, but useful & easy to add. Just call
     DEBUG_STUFF_INIT at startup, then insert a DEBUG_ENTRY wherever you
     want. To see what's up, especially during a bad hang, just dm [the
     address that DEBUG_STUFF_INIT dumped out at startup.]

     The conditional compilation means that if you turn of debugging in your
     final build, the release version of the program won't have any of this
     code in it.

     [debugstuff.h]
     #define DB_ROUTINES_NBR_ENTRIES 40
     #define DB_ROUTINES_CHARS               16
     typedef char                    db_routine_entry[DB_ROUTINES_CHARS];
     #if DEBUGGING
     void DEBUG_ENTRY (char *txt);
     void DEBUG_STUFF_INIT (char *title);
     #else
     #define DEBUG_ENTRY
     #define DEBUG_STUFF_INIT
     #endif

     [debugstuff.c]
     ///////////////////////////////////////////////////////////////////////
     ////
     #if DEBUGGING
     void DEBUG_STUFF_INIT (char *title) 
             OSErr                   myErr;
             char                    txt[256];
             long                    response;

             if (!MacsBugInstalled()) 
                     hi_notify ("MacsBug is not installed… the debugging log will be
     inaccessible.");

             g_debug_entries= (db_routine_entry*) NewPtrClear
     ((DB_ROUTINES_NBR_ENTRIES+2) * DB_ROUTINES_CHARS);
             memset (&g_debug_entries[0], '=', DB_ROUTINES_CHARS);
             BlockMoveData (title, &g_debug_entries[0], strlen(title));
             memset (&g_debug_entries[DB_ROUTINES_NBR_ENTRIES+1], '=',
     DB_ROUTINES_CHARS);
             sprintf (txt, "Debugging routine list is at 0x%lx;g", (long)
     g_debug_entries);
             c2pstr (txt);
             DebugStr (txt);
     
     #endif

     ///////////////////////////////////////////////////////////////////////
     ////
     //      This leaves a line in the debugging entry log.
     //      For example, important enter/exit points of routines
     ///////////////////////////////////////////////////////////////////////
     ////
     #if DEBUGGING
     void DEBUG_ENTRY (char *txt) 
             short                   len;

             //      Move the previous entries down one
             BlockMoveData (&g_debug_entries[1], &g_debug_entries[2],
                             (DB_ROUTINES_NBR_ENTRIES-1) * DB_ROUTINES_CHARS);

             //      Clear the new space
             memset (&g_debug_entries[1], 0, DB_ROUTINES_CHARS);

             //      Copy in the new entry
             len= strlen (txt);
             if (len > DB_ROUTINES_CHARS) 
                     len= DB_ROUTINES_CHARS;
             
             BlockMoveData (txt, &g_debug_entries[1], len);
     
     #endif

     Hope this does someone some good.

     - Darren

     ==========================================================================
     Darren Giles, Technical Director                        Terran Interactive
     For info on Cleaner QuickTime compression, visit http://www.terran-int.com

Logging Data in Real Time

     Contributed by Dave Stone, dstone@chem.utoronto.ca

     I've also used conditional compilation to debug serial communications
     stuff being processed at interrupt time - something like

     #ifdef DEBUG_MY_ROUTINE
      #define MAX_BUFFER  10000
             char bufffer[MAX_BUFFER];               //      or NewPtr it or something
      long bufCount = 0L;
     #endif
     .
     .
     .
     #ifdef DEBUG_MY_ROUTINE
             if(bufCount <  MAX_BUFFER) 
                     bufCount ++;
             buffer[bufCount] = ch;                  //      ch is a character read/written through serial
     port
             
     #endif

     etc. Handy, because you can let it rip for a while to see if there is a
     consistent pattern in the errors in ch - in my case, a stream of Midi
     data through a very basic freeware Midi Driver.

Using Cursors to Trace Program Execution

     Contributed by Tom Kimpton, Jostens Learning Corporation, tom@jlc.com

     One technique that I have used in the past where dropping into the
     debugger wasn't an option, and logging wasn't getting flushed in
     time/took too long, was to create a bunch of cursors numbering 00 - 99,
     and made a call to set the cursor and return the number of the previous
     cursor:

     routine1()
     
     short oldCursor = setDebugCursor(15);
         ...
         (void) setDebugCursor(oldCursor);

     This way when the machine froze, the cursor would tell me what routine
     it had frozen in.

Using Touch and Go Breakpoints with Two Monitors

     I had a bug in which the Mac would occasionally freeze during shutdown
     without the ability to get into Macsbug. It would only occur about once
     in twenty reboots.

     The way I dealt with this was to borrow a display card and hook two
     monitors up to the Macintosh. You can use the Monitors control panel to
     select which monitor will be used for Macsbug (hold the option key and
     drag the happy Mac around).

     I wrote a small application that just called ShutDownRestart(), and
     placed it in the Startup Items folder. Thus, when the Mac came up into
     the Finder it would immediately reboot. About every twenty minutes it
     would freeze.

     If you define a macro named FirstTime in the Debugger Prefs file,
     Macsbug will execute it when it loads. I used a macro that was
     something like:

     swap; atr; atb shutdownrestart ";atb Newhandle ";g";g";g

     or some such. The swap command caused Macsbug to be permanently left on
     the second screen. That way when the crash occurred you could still see
     the last few things Macsbug did. The ";g" following the a-trap break
     commands tells Macsbug to continue after the break - this is a "Touch
     and Go" breakpoint.

     One thing you can also do inside a touch and go breakpoint is set new
     breakpoints. I would take guesses on what traps might be called in the
     neighborhood of the crash, and have breakpoints set on them when
     ShutDownRestart was called.

     Then I could leave the Mac rebooting on its own in the lab, and pop in
     every half an hour to check the log, adjust the breakpoints and start
     it up again.

     The actual bug took about five months to find and fix.

Twiddling Pixels in the Menu Bar

     Contributed by Dave Fleck, Wacom Technology Corp., dfleck@wacom.com

     Here's my debugging tip.

     I do drivers, and you just plain can't set a breakpoint in ADB
     completion routines (freezes the keyboard so MacsBug is worthless!).

     So I throw one of the routines below into the routine to see when a
     piece of code gets executed.

     What does it do? It "lights up" a bar (length dependant on screen
     resolution) in the menu bar. So if you DotToggle(300); you get a
     flashing short line in the menu bar.

     void DotOn(long where) 
        long  *dot;
        dot = (long *)(LMGetScrnBase() + where);
        *dot |= -1;
     
     void DotOff(long where) 
        long  *dot;
        dot = (long *)(LMGetScrnBase() + where);
        *dot &= 0;
     
     void DotToggle(long where) 
        long  *dot;
        dot = (long *)(LMGetScrnBase() + where);
        *dot ^= -1;

     dave

     -----------------------------------------------------------------
     Dave Fleck   email:dfleck@wacom.com      phone:360-750-8882x154
     Wacom Technology Corp.                          sales@wacom.com
     501 S.E. Columbia Shores Blvd, #300           support@wacom.com
     Vancouver, WA 98661                           WWW/FTP:wacom.com
     ------------------------------------------------------------------

Forcing Testers to Use Macsbug During Beta Testing

     Contributed by Harold Ekstrom, the ag group, inc., ekstrom@aggroup.com.

     Don't you just hate it when beta testers say "it crashes" but don't
     give you any more information? First, tell them to use the "stdlog"
     command in MacsBug, then force them to install MacsBug by checking for
     it during your program's initialization:

     --- DebugUtils.h ---

     #pragma once

     // Debugger types.
     typedef enum DebuggerType 
         kNoDebugger,
         kMacsBug,
         kTMON,
         kOtherDebugger
      DebuggerType;

     Boolean     GetDebuggerInfo( DebuggerType *outDebuggerType,
                     UInt16 *outDebuggerSignature );

     --- DebugUtils.c ---

     // Private defines for some low memory globals.
     #define MacJmp      ((Ptr *)0x0120)     // MacsBug jumptable [pointer].
     #define MacJmpByte  ((UInt8 *)0x0120)   // MacsBug flags in 24 bit mode [byte].
     #define MacJmpFlag  ((UInt8 *)0x0BFF)   // MacsBug flag [byte].

     // Debugger flag bits.
     #define kDebuggerInstalledBit   5

     //
     ---------------------------------------------------------------------------------
     //
     //
     ---------------------------------------------------------------------------------

     Boolean
     GetDebuggerInfo(
         DebuggerType *  outDebuggerType,
         UInt16 *        outDebuggerSignature )
     
         Boolean theResult = false;
         SInt32  theResponse;

         // Initialize return values to defaults.
         *outDebuggerType = kNoDebugger;
         *outDebuggerSignature = '  ';

         if ( Gestalt( gestaltAddressingModeAttr, &theResponse ) == noErr ) 

             UInt16  theDebugFlags;

             // As documented in the "Macsbug Reference & Debugging Guide", page 412
             // if we have a 32 bit capable Memory Manager, debugger flags are at 0x0BFF
             // if we have a 24 bit capable Memory Manager, debugger flags are at 0x0120

             if ( (theResponse & (1L << gestalt32BitCapable)) != 0 ) 
                 theDebugFlags = *MacJmpFlag;
              else 
                 theDebugFlags = *MacJmpByte;

             if ( (theDebugFlags & (1L << kDebuggerInstalledBit)) != 0 ) 

                 Ptr theDebuggerEntry;
                 Ptr theROMBaseWorld;

                 // There is a debugger installed.
                 theResult = true;

                 // Get the debugger entry.
                 theDebuggerEntry = StripAddress( *MacJmp );

                 // Get the ROM base.
                 theROMBaseWorld = StripAddress( LMGetROMBase() );

                 // Compare the debugger entry to the ROM base.
                 if ( theDebuggerEntry < theROMBaseWorld ) 

                     UInt16  **theDebuggerWorld;

                     // It's not a ROM based debugger.
                     // Get the debugger world.
                     theDebuggerWorld = (UInt16 **) StripAddress( theDebuggerEntry - sizeof(Ptr) );

                     // Get the debugger signature.
                     *outDebuggerSignature = **theDebuggerWorld;

                     // Get the debugger type.
                     switch ( *outDebuggerSignature ) 

                         case 'MT':
                             *outDebuggerType = kMacsBug;
                             break;

                         case 'WH':
                             *outDebuggerType = kTMON;
                             break;

                         default:
                             *outDebuggerType = kOtherDebugger;
                             break;

                 

         

         return theResult;

     Check for a low level debugger like this:

     #if BETA_VERSION
             DebuggerType    theDebuggerType;
             UInt16          theDebuggerSig;
             if ( !GetDebuggerInfo( &theDebuggerType, &theDebuggerSig ) ) 
                 HaltRotateCursor( gRotateCrsr );
                 StopAlert( go_get_macsbug_alrt, nil );
                 ExitToShell();
             
     #endif

The Developer University Debugging Class

     Contributed by Malcolm Teas, Blaze Technology, mhteas@btech.com

     As the instructor and developer of Apple's Developer University class
     called "Macintosh Debugging Tips and Techniques" I would like to make
     sure your tips page references this class.

     This class is centered around MacsBug as the easiest to learn low-level
     debugger. It also covers a multitude of low-level topics like memory
     maps, subroutine calling protocols, code segments and code fragments,
     reading (and understanding) assembler for 68K and PPC, and many more
     areas. One key area is how to avoid bugs in the first place. All the
     information you need to be able to debug software at the low-level.

     The class is available from Apple's Developer University.

     Another thing I want to mention is the version number of the most
     current MacsBug is 6.5.3 (as of this writing). This version includes
     the PPC commands and features.

     [I have taken this class and recommend it highly - Mike]

Links to Other Macsbug Pages

        o Cool MacsBug Tricks (an informal guide)
        o Macsbug Use
        o Develop Issue 22: Balance of Power: MacsBug for PowerPC
        o Macsbug 6.5.2 Help
        o Macintosh Programming Books
        o Guide to Mac Tools
        o Macintosh Technical Q&A's
        o comp.sys.mac.programming FAQ
        o Macintosh Testing and Quality Control Tools
        o Development and Debugging Tools for the Macintosh